package org.madgecko.basic.dao;

import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.madgecko.basic.model.Pager;
import org.madgecko.basic.model.SystemContext;

public class BaseDao<T> implements IBaseDao<T> {

    @Inject
    private SessionFactory sessionFactory;
    private Class<?> clz;

    public Class<?> getClz() {
        if (clz == null) {
            // 获取泛型的Class对象
            clz = ((Class<?>) (((ParameterizedType) (this.getClass()
                    .getGenericSuperclass())).getActualTypeArguments()[0]));
        }
        return clz;
    }

    protected Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    @Override
    public T add(T t) {
        getSession().save(t);
        return t;
    }

    @Override
    public T update(T t) {
        getSession().update(t);
        return t;
    }

    public void delete(int id) {
        getSession().delete(this.load(id));
    }

    @Override
    public void delete(T t) {
        getSession().delete(t);
    }

    @Override
    public T load(int id) {
        return (T) getSession().load(getClz(), id);
    }

    public <N extends Object>List<N> list(String hql, Object[] args) {
        return this.list(hql, args, null);
    }

    public <N extends Object>List<N> list(String hql, Object arg) {
        return this.list(hql, new Object[] { arg });
    }

    public <N extends Object>List<N> list(String hql) {
        return this.list(hql, null, null);
    }

    public <N extends Object>List<N> list(String hql, Map<String, Object> alias) {
        return this.list(hql, null, alias);
    }

    private String initSort(String hql) {
        String order = SystemContext.getOrder();
        String sort = SystemContext.getSort();

        if (sort != null && !"".equals(sort.trim())) {
            hql += " order by " + sort;
            if ("desc".equalsIgnoreCase(order)) {
                hql += " desc";
            } else {
                hql += " asc";
            }
        }
        return hql;
    }

    @SuppressWarnings("rawtypes")
    private void setAliasParameter(Query query, Map<String, Object> alias) {
        if (alias != null) {
            Set<String> keys = alias.keySet();
            for (String key : keys) {
                Object val = alias.get(key);
                if (val instanceof Collection) {
                    query.setParameterList(key, (Collection) val);
                } else {
                    query.setParameter(key, val);
                }
            }
        }
    }

    private void setParameter(Query query, Object[] args) {
        if (args != null && args.length > 0) {
            int index = 0;
            for (Object arg : args) {
                query.setParameter(index++, arg);
            }
        }
    }

    public <N extends Object>List<N> list(String hql, Object[] args, Map<String, Object> alias) {
        // hql不会被修改，视频里错误
        String sortedHql = initSort(hql);
        Query query = getSession().createQuery(sortedHql);
        setAliasParameter(query, alias);
        setParameter(query, args);
        return query.list();
    }

    public Pager<T> find(String hql, Object[] args) {
        return this.find(hql, args, null);
    }

    public Pager<T> find(String hql, Object arg) {
        return this.find(hql, new Object[] { arg });
    }

    public Pager<T> find(String hql) {
        return this.find(hql, null, null);
    }

    private String getCountHql(String hql, boolean isHql) {
        int formIndex = hql.indexOf("from");
        String e = hql.substring(formIndex);
        String c = "select count(*) " + e;
        if (isHql) {
            c.replace("fetch", "");
        }
        return c;
    }

    @SuppressWarnings("rawtypes")
    private void setPagers(Query query, Pager pages) {
        Integer pageSize = SystemContext.getPageSize();
        Integer pageOffset = SystemContext.getPageOffset();
        if (null == pageOffset || pageOffset < 0) {
            pageOffset = 0;
        }
        if (null == pageSize || pageSize < 0) {
            // XXX 待写成全局变量
            pageSize = 15;
        }
        pages.setSize(pageSize);
        pages.setOffset(pageOffset);
        query.setFirstResult(pageOffset).setMaxResults(pageSize);
    }

    public Pager<T> find(String hql, Object[] args, Map<String, Object> alias) {
        hql = initSort(hql);
        Query query = getSession().createQuery(hql);
        String countHql = getCountHql(hql, true);
        Query countQuery = getSession().createQuery(countHql);
        setAliasParameter(query, alias);
        setAliasParameter(countQuery, alias);
        setParameter(query, args);
        setParameter(countQuery, args);
        Pager<T> pages = new Pager<T>();
        setPagers(query, pages);
        List<T> datas = query.list();
        pages.setDatas(datas);
        Long total = (Long)countQuery.uniqueResult();
        pages.setTotal(total);
        return pages;
    }

    public Pager<T> findByAlias(String hql, Map<String, Object> alias) {
        return this.find(hql, null, alias);
    }

    public Object queryObject(String hql, Object[] args,
                              Map<String, Object> alias) {
        Query query = getSession().createQuery(hql);
        setAliasParameter(query, alias);
        setParameter(query, args);
        return query.uniqueResult();
    }

    public Object queryObject(String hql, Map<String, Object> alias) {
        return this.queryObject(hql, null, alias);
    }

    public Object queryObject(String hql, Object[] args) {
        return this.queryObject(hql, args, null);
    }

    public Object queryObject(String hql, Object arg) {
        return this.queryObject(hql, new Object[] { arg }, null);
    }

    public Object queryObject(String hql) {
        return this.queryObject(hql, null, null);
    }

    public void updateByHql(String hql, Object[] args) {
        Query query = getSession().createQuery(hql);
        setParameter(query, args);
        query.executeUpdate();
    }

    public void updateByHql(String hql, Object arg) {
        this.updateByHql(hql, new Object[] { arg });
    }

    public void updateByHql(String hql) {
        this.updateByHql(hql, null);
    }

    public <N extends Object>List<N> listBySql(String sql, Object[] args, Class<?> clz,
                                               boolean hasEntity) {
        return this.listBySql(sql, args, null, clz, hasEntity);
    }

    public <N extends Object>List<N> listBySql(String sql, Object arg, Class<?> clz,
                                               boolean hasEntity) {
        return this.listBySql(sql, new Object[] { arg }, null, clz, hasEntity);
    }

    public <N extends Object>List<N> listBySql(String sql, Class<?> clz,
                                               boolean hasEntity) {
        return this.listBySql(sql, null, null, clz, hasEntity);
    }

    public <N extends Object>List<N> listBySql(String sql, Object[] args,
                                               Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
        sql = initSort(sql);
        SQLQuery sq = getSession().createSQLQuery(sql);
        setAliasParameter(sq, alias);
        setParameter(sq, args);
        if (hasEntity) {
            sq.addEntity(clz);
        } else {
            sq.setResultTransformer(Transformers.aliasToBean(clz));
        }
        return sq.list();
    }

    public <N extends Object>List<N> listBySql(String sql, Map<String, Object> alias,
                                               Class<?> clz, boolean hasEntity) {
        return this.listBySql(sql, null, alias, clz, hasEntity);
    }

    public <N extends Object>Pager<N> findBySql(String sql, Object[] args,
                                                Class<?> clz, boolean hasEntity) {
        return this.findBySql(sql, args, null, clz, hasEntity);
    }

    public <N extends Object>Pager<N> findBySql(String sql, Object arg, Class<?> clz,
                                                boolean hasEntity) {
        return this.findBySql(sql, new Object[]{arg}, clz, hasEntity);
    }

    public <N extends Object>Pager<N> findBySql(String sql, Class<?> clz,
                                                boolean hasEntity) {
        return this.findBySql(sql, null, null, clz, hasEntity);
    }

    public <N extends Object>Pager<N> findBySql(String sql, Object[] args,
                                                Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
        sql = initSort(sql);
        String countSql = getCountHql(sql, false);
        SQLQuery sqlQuery = getSession().createSQLQuery(sql);
        setAliasParameter(sqlQuery, alias);
        setParameter(sqlQuery, args);

        SQLQuery countQuery = getSession().createSQLQuery(countSql);
        setAliasParameter(countQuery, alias);
        setParameter(countQuery, args);
        Pager<N> pages = new Pager<N>();
        setPagers(sqlQuery, pages);
        if(hasEntity){
            sqlQuery.addEntity(clz);
        }else{
            sqlQuery.setResultTransformer(Transformers.aliasToBean(clz));
        }
        List<N> datas = sqlQuery.list();
        BigInteger total = (BigInteger)countQuery.uniqueResult();
        pages.setDatas(datas);
        pages.setTotal(total.longValue());
        return pages;
    }

    public <N extends Object>Pager<N> findBySql(String sql, Map<String, Object> alias,
                                                Class<?> clz, boolean hasEntity) {
        return this.findBySql(sql, null, alias, clz, hasEntity);
    }

    /**
     * 获取主键系列函数
     * @param hql
     * @param args
     * @param alias
     * @return
     */
    public List<Integer> listIdByHql(String hql, Object[] args, Map<String, Object> alias){
        Query query = getSession().createQuery(hql);
        setAliasParameter(query, alias);
        setParameter(query, args);
        return query.list();
    }

    public List<Integer> listIdByHql(String hql){
        Query query = getSession().createQuery(hql);
        return query.list();
    }

    public List<Integer> listIdByHql(String hql, Object arg){
        Query query = getSession().createQuery(hql);
        setParameter(query, new Object[]{arg});
        return query.list();
    }

    public List<Integer> listIdByHql(String hql, Object[] args){
        Query query = getSession().createQuery(hql);
        setParameter(query, args);
        return query.list();
    }

//	public <N extends Object>List<N> listByHql(String hql, Object[] args, Map<String, Object> alias) {
//		// hql不会被修改，视频里错误
//		String sortedHql = initSort(hql);
//		Query query = getSession().createQuery(sortedHql);
//		setAliasParameter(query, alias);
//		setParameter(query, args);
//		return query.list();
//	}
//
//	public <N extends Object>List<N> listByHql(String hql, Object[] args) {
//		return this.listByHql(hql, args, null);
//	}
//
//	public <N extends Object>List<N> listByHql(String hql, Object arg) {
//		return this.listByHql(hql, new Object[] { arg });
//	}
//
//	public <N extends Object>List<N> listByHql(String hql) {
//		return this.listByHql(hql, null, null);
//	}
//
//	public <N extends Object>List<N> listByHql(String hql, Map<String, Object> alias) {
//		return this.listByHql(hql, null, alias);
//	}
}
